﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace katOS
{
    /// <summary>
    /// Class that represents the scheduler of the OS
    /// </summary>
    class Scheduler
    {
        public volatile static LinkedList<InterfaceQueue<Process>> queue_list = new LinkedList<InterfaceQueue<Process>>();

        public volatile static LinkedList<Process> process_to_kill = new LinkedList<Process>();
        public volatile static LinkedList<Process> wating_queue = new LinkedList<Process>();
        public volatile static LinkedList<Process> all_processes = new LinkedList<Process>();
        public volatile static LinkedList<Process> terminated_processes = new LinkedList<Process>();
        public volatile static LinkedList<Process> processes_to_add = new LinkedList<Process>();

        //Temporal list
        public static LinkedList<Process> temp_processes_list = new LinkedList<Process>();

        public volatile static LinkedList<Semaphore> semaphores_list = new LinkedList<Semaphore>();

        public volatile static LinkedList<int> waiting_time = new LinkedList<int>();
        public volatile static LinkedList<int> waiting_io_queue = new LinkedList<int>();
       
        public static int wait = 5;
        public volatile static int ticks_counter = 0;
        public volatile static int sync_process_counter = 0;
        public volatile static int start_queue = -1;
        
        Interpreter an_interpreter; //an instance of the interpreter
        static Parser a_parser; //an instance of the parser
        public volatile static int pc; //process counter

        //ON STATISTICS ADD SEMAPHORES
        
        /// <summary>
        /// Method that creates a new instance of the scheduler
        /// </summary>
        public Scheduler()
        {
            ReadXML("syssemaforo.xml");
            an_interpreter = new Interpreter();
            pc = 1;
        }

        public static void AddProcessToTemp(Process a_process)
        {
            temp_processes_list.AddLast(a_process);
            moveToRealQueue();
        }

        public static void moveToRealQueue()
        {
            for(int i=0; i<temp_processes_list.Count; i++)
            {
                a_parser = new Parser();
                temp_processes_list.ElementAt(i).setInstructions(a_parser.getInstructionSet(temp_processes_list.ElementAt(i), temp_processes_list.ElementAt(i).getFileName()));
                if (temp_processes_list.ElementAt(i).getSync())
                {
                    sync_process_counter++;
                }
                all_processes.AddLast(temp_processes_list.ElementAt(i));
                temp_processes_list.ElementAt(i).setQueue(start_queue);
                queue_list.ElementAt(start_queue).addProcess(temp_processes_list.ElementAt(i));
                temp_processes_list.Remove(temp_processes_list.ElementAt(i));
            }
            
        }
        
        
        public void ContextSwitchQuantumEnd(Process process)
        {
            if (process != null)
            {
                int current_queue = process.getQueue();
                process.setState(8);
                int to_queue = queue_list.ElementAt(current_queue).getQueueOnQuantumEnd();
                process.setQueue(queue_list.ElementAt(current_queue).getQueueOnQuantumEnd());
                if (!queue_list.ElementAt(current_queue).isEmpty())
                {
                    queue_list.ElementAt(to_queue).addProcess(queue_list.ElementAt(current_queue).removeFirst()); 
                }
            }
        }

        public void ContextSwitchIO(Process process)
        {
            int current_queue = process.queue;

            wating_queue.AddLast(queue_list.ElementAt(current_queue).removeFirst());
            waiting_time.AddLast(wait);
            process.setQueue(queue_list.ElementAt(current_queue).getQueueOnIOEnd());
            waiting_io_queue.AddLast(queue_list.ElementAt(current_queue).getQueueOnIOEnd());
        }

        public void UpdateWaitingList()
        {
            for (int i = 0; i < wating_queue.Count; i++)
            {
                int temp_wait = waiting_time.ElementAt(i);
                waiting_time.Remove(i);
                waiting_time.AddBefore(waiting_time.Find(waiting_time.ElementAt(i+1)),--temp_wait);
                if (waiting_time.ElementAt(i) == 0)
                {
                    int to_queue = waiting_io_queue.ElementAt(i);
                    Process a_process = wating_queue.ElementAt(i);
                    queue_list.ElementAt(to_queue).addProcess(a_process);
                    a_process.io_wait = false;
                    a_process.quantum_over = false;
                    
                    wating_queue.Remove(wating_queue.ElementAt(i));
                    waiting_time.Remove(waiting_time.ElementAt(i));
                    waiting_io_queue.Remove(waiting_io_queue.ElementAt(i));
                    i--;
                }
            }
        }

        public int getProcessQueue()
        {
            for (int i = 0; i < queue_list.Count; i++)
            {
                if (!queue_list.ElementAt(i).isEmpty())
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// Method that excecutes the processes
        /// </summary>
        public void Run()
        {
            //Console.WriteLine("is entering the run of the shceduler");
            while (true)
            {
                ticks_counter++;
                UpdateWaitingList();
                //moveToRealQueue();

                int current_queue = getProcessQueue();
                Process process = null;

                if (current_queue!=-1)
                {
                    //Console.WriteLine("is getting the first element of the current queue");
                    process = queue_list.ElementAt(current_queue).getFirst();
                }
                if(process!=null)
                {
                    bool temp = Execute(process);
                    if (temp)
                    {
                        Process a_process = queue_list.ElementAt(process.getQueue()).removeFirst();
                        terminated_processes.AddLast(a_process);
                        if (a_process.getSync())
                        {
                            sync_process_counter--;
                        }
                    }

                    if (!(process_to_kill.Count == 0))
                    {
                        killProcesses();
                    }
                    process.subQuantum();

                    if(process.io_wait && !temp)
                    {
                        process.quantum_over=false;
                        ContextSwitchIO(process);
                    }
                    if(process.quantum_over && !temp)
                    {
                        ContextSwitchQuantumEnd(process);
                    }
                    //update statistics
                }
            }
        }

        public void killProcesses()
        {
            process_to_kill.ElementAt(0).setState(6);
            for(int i=0; i<queue_list.Count; i++)
            {
                if(queue_list.ElementAt(i).getQueue().Contains(process_to_kill.ElementAt(0)))
                {
                    queue_list.ElementAt(i).Remove(process_to_kill.ElementAt(0));
                    process_to_kill.RemoveFirst();
                }
            }

        }

        public static bool hasSync()
        {
            if (sync_process_counter > 0)
            {
                return true;
            }
            else if (sync_process_counter == 0)
            {
                return false;
            }
            return false;
        }

        /// <summary>
        /// Method that excecutes a line
        /// </summary>
        /// <returns>Returns the line to be excecuted</returns>
        public bool Execute(Process process)
        { 
            return an_interpreter.CheckEnd(queue_list.ElementAt(process.getQueue()).Peek());     
        }

        private void ReadXML(string xmlName)
        {
            XmlTextReader xmlReader = new XmlTextReader("C:/Users/kareAndy/Documents/UVG/Ciclo 5/CC3002 Sistemas Operativos/Proyecto No. 2/"+xmlName);


            while (xmlReader.Read())
            {
                switch (xmlReader.NodeType)
                {
                    case XmlNodeType.Element: // The node is an element.
                        if (xmlReader.Name == "scheduling")
                        {
                            start_queue = int.Parse(xmlReader.GetAttribute("startQueue"))-1;
                            //Console.WriteLine("Start Queue: "+startQueue);
                        }
                        if (xmlReader.Name == "queue")
                        {
                            AbstractQueue<Process> new_queue = null;
                            int id = int.Parse(xmlReader.GetAttribute("id"));
                            string name = xmlReader.GetAttribute("name");
                            string type = xmlReader.GetAttribute("type");
                            int quantum = int.Parse(xmlReader.GetAttribute("quantum"));
                            int queueOnEnd = int.Parse(xmlReader.GetAttribute("queueOnQuantumEnd"))-1;
                            int queueOnIOEnd = int.Parse(xmlReader.GetAttribute("queueOnIOEnd"))-1;
                            int preemptive = int.Parse(xmlReader.GetAttribute("preemptive"));
                            bool temp_preemptive;
                            if (preemptive == 0)
                            {
                                temp_preemptive = false;
                            }
                            else
                            {
                                temp_preemptive = true;
                            }
                            //onsole.WriteLine(id + name + type + quantum + queueOnEnd + queueOnIOEnd + preemptive);

                            switch (type)
                            {
                                case "Priority":
                                    new_queue = new PriorityQueue(id, name, quantum, temp_preemptive, queueOnEnd, queueOnIOEnd);
                                    break;

                                case "FCFS":
                                    new_queue = new FCFS(id, name, quantum, temp_preemptive, queueOnEnd, queueOnIOEnd);
                                    break;

                                case "SJF":
                                    new_queue = new SJF(id, name, quantum, temp_preemptive, queueOnEnd, queueOnIOEnd);
                                    break;

                            }
                            queue_list.AddLast(new_queue);
                        }
                        break;
                }
            }
        }
    }
}
